scrolledwindow: Cancel overshoot on dimension changes
authorAlexander Mikhaylenko <alexm@gnome.org>
Mon, 15 Mar 2021 12:17:01 +0000 (17:17 +0500)
committerAlexander Mikhaylenko <alexm@gnome.org>
Thu, 1 Apr 2021 11:37:30 +0000 (16:37 +0500)
If we scroll down in a list that's still being filled, we hit the edge and
initiate overshoot, and then the adjustment's upper value increases. This
leads to an unwanted bounce back.

Additionally, if in a similar situation the upper value decreases, the
overscroll glow gets stuck.

Update kinetic scrolling upper and lower value on changes, and immediately
cancel it if dimensions on that side change.

Fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/3752

gtk/gtkkineticscrolling.c
gtk/gtkkineticscrollingprivate.h
gtk/gtkscrolledwindow.c

index eb9c13bdfd3d2779f22d5ae0da8ecfc6ba5a9644..87618acd7c60c7cce5a4fb4be0038f89abdeab2b 100644 (file)
@@ -123,6 +123,35 @@ gtk_kinetic_scrolling_new (double lower,
   return data;
 }
 
+GtkKineticScrollingChange
+gtk_kinetic_scrolling_update_size (GtkKineticScrolling *data,
+                                   double               lower,
+                                   double               upper)
+{
+  GtkKineticScrollingChange change = GTK_KINETIC_SCROLLING_CHANGE_NONE;
+
+  if (lower != data->lower)
+    {
+      if (data->position <= lower)
+        change |= GTK_KINETIC_SCROLLING_CHANGE_LOWER;
+
+      data->lower = lower;
+    }
+
+  if (upper != data->upper)
+    {
+      if (data->position >= data->upper)
+        change |= GTK_KINETIC_SCROLLING_CHANGE_UPPER;
+
+      data->upper = upper;
+    }
+
+  if (data->phase == GTK_KINETIC_SCROLLING_PHASE_OVERSHOOTING)
+    change |= GTK_KINETIC_SCROLLING_CHANGE_IN_OVERSHOOT;
+
+  return change;
+}
+
 void
 gtk_kinetic_scrolling_free (GtkKineticScrolling *kinetic)
 {
index b97fac360f2759f661a752df56a339445252d85f..a531ace4768ea478280084ad11decee2d97bc88e 100644 (file)
 
 G_BEGIN_DECLS
 
+typedef enum {
+  GTK_KINETIC_SCROLLING_CHANGE_NONE = 0,
+  GTK_KINETIC_SCROLLING_CHANGE_LOWER = 1 << 0,
+  GTK_KINETIC_SCROLLING_CHANGE_UPPER = 1 << 1,
+  GTK_KINETIC_SCROLLING_CHANGE_IN_OVERSHOOT = 1 << 2,
+} GtkKineticScrollingChange;
+
 typedef struct _GtkKineticScrolling GtkKineticScrolling;
 
 GtkKineticScrolling *    gtk_kinetic_scrolling_new  (double                lower,
@@ -34,6 +41,10 @@ GtkKineticScrolling *    gtk_kinetic_scrolling_new  (double                lower
                                                      double                initial_velocity);
 void                     gtk_kinetic_scrolling_free (GtkKineticScrolling  *kinetic);
 
+GtkKineticScrollingChange gtk_kinetic_scrolling_update_size (GtkKineticScrolling *data,
+                                                             double               lower,
+                                                             double               upper);
+
 gboolean                 gtk_kinetic_scrolling_tick (GtkKineticScrolling  *data,
                                                      double                time_delta,
                                                      double               *position,
index a56891b5e21c383265fad6c97fedbbe5a6d53940..40e285c77b3e02f27bc4bea62e661717eff73e20 100644 (file)
@@ -3455,6 +3455,24 @@ gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
 
           if (priv->hscrollbar_visible != visible)
             gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
+
+          if (priv->hscrolling)
+            {
+              GtkKineticScrollingChange change;
+              double lower = gtk_adjustment_get_lower (adjustment);
+              double upper = gtk_adjustment_get_upper (adjustment);
+              upper -= gtk_adjustment_get_page_size (adjustment);
+
+              change = gtk_kinetic_scrolling_update_size (priv->hscrolling, lower, upper);
+
+              if ((change & GTK_KINETIC_SCROLLING_CHANGE_IN_OVERSHOOT) &&
+                  (change & (GTK_KINETIC_SCROLLING_CHANGE_UPPER | GTK_KINETIC_SCROLLING_CHANGE_LOWER)))
+                {
+                  g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free);
+                  priv->unclamped_hadj_value = gtk_adjustment_get_value (adjustment);
+                  gtk_scrolled_window_invalidate_overshoot (scrolled_window);
+                }
+            }
         }
     }
   else if (adjustment == gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar)))
@@ -3468,8 +3486,29 @@ gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
 
           if (priv->vscrollbar_visible != visible)
             gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
+
+          if (priv->vscrolling)
+            {
+              GtkKineticScrollingChange change;
+              double lower = gtk_adjustment_get_lower (adjustment);
+              double upper = gtk_adjustment_get_upper (adjustment);
+              upper -= gtk_adjustment_get_page_size (adjustment);
+
+              change = gtk_kinetic_scrolling_update_size (priv->vscrolling, lower, upper);
+
+              if ((change & GTK_KINETIC_SCROLLING_CHANGE_IN_OVERSHOOT) &&
+                  (change & (GTK_KINETIC_SCROLLING_CHANGE_UPPER | GTK_KINETIC_SCROLLING_CHANGE_LOWER)))
+                {
+                  g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free);
+                  priv->unclamped_vadj_value = gtk_adjustment_get_value (adjustment);
+                  gtk_scrolled_window_invalidate_overshoot (scrolled_window);
+                }
+            }
         }
     }
+
+  if (!priv->hscrolling && !priv->vscrolling)
+    gtk_scrolled_window_cancel_deceleration (scrolled_window);
 }
 
 static void